home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 June / Macworld (1999-06).dmg / Shareware World / Info / For Developers / MacZoop2.0.sea / MacZoop2.0 / Required Classes / ZMenuBar.cpp < prev    next >
Text File  |  1999-02-11  |  38KB  |  1,491 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZMenuBar.cpp        -- the menubar manager object
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22. #include    "MacZoop.h"
  23. #include    "ProjectSettings.h"
  24. #include    "ZCommander.h"
  25.  
  26. #ifndef __BALLOONS__
  27. #include    <balloons.h>
  28. #endif
  29.  
  30.  
  31. extern    ZCommander*        gCurHandler;
  32.  
  33. short        gFontMenuID = 0;
  34.  
  35. /*--------------------------------***  DESTRUCTOR  ***---------------------------------*/
  36.  
  37.  
  38. ZMenuBar::~ZMenuBar()
  39. {
  40.     // menubar must be visible when we quit
  41.     
  42.     if ( mbHiding == MBAR_HIDE )
  43.         ShowHideMenuBar( MBAR_SHOW );
  44.     
  45.     if ( theMenuCmds )
  46.         ForgetObject( theMenuCmds );
  47.         
  48.     if ( theMenus )
  49.         ForgetObject( theMenus );
  50.  
  51.     ReleaseResource((Handle) mBarH );
  52. }    
  53.  
  54.  
  55. /*--------------------------------***  INITMENUBAR  ***--------------------------------*/
  56. /*
  57. initialise the menubar from a MBAR resource
  58. ---------------------------------------------------------------------------------------*/
  59.  
  60. void        ZMenuBar::InitMenuBar()
  61. {
  62.     // create an array for holding our command info
  63.     
  64.     FailNIL( theMenuCmds = new ZArray( sizeof( MenuCmd )));
  65.     FailNIL( theMenus = new ZArray( sizeof( MenuInfRec )));
  66.     
  67.     mbCount = 0;
  68.     miSeed = 1;
  69.     wmMenuID = 0;
  70.     rbPending = FALSE;
  71.     inDispatch = FALSE;
  72.     
  73.     // initially menubar is visible
  74.     
  75.     mBarHeight = GetMBarHeight();
  76.     mbHiding = MBAR_SHOW;
  77.     
  78.     // load the MBAR resource
  79.     
  80.     mBarH = (short**) GetResource( 'MBAR', mBarID );
  81.     FailOSErr( ResError());
  82.     
  83.     HNoPurge((Handle) mBarH );
  84.     
  85.     // read in menus from the MBAR resource
  86.     
  87.     LoadMenus();
  88.     
  89.     HPurge((Handle) mBarH );
  90.     
  91.     // add the standard items (DA's) to the Apple menu
  92.     
  93.     AppendStdItems( kAppleMenuID );    
  94.     
  95.     menuCheckChar = checkMark;
  96.     
  97.     // note how many items are in the help menu so we can correctly
  98.     // identify any items we have added
  99.     
  100.     MenuHandle    helpMenuH;
  101.     
  102.     FailOSErr( HMGetHelpMenuHandle( &helpMenuH )); 
  103.     mHelpOffset = CountMenuItems( helpMenuH );
  104. }    
  105.  
  106.  
  107. /*-------------------------------***  CLICKMENUBAR  ***--------------------------------*/
  108. /*
  109. handle mouse click in the menubar
  110. ---------------------------------------------------------------------------------------*/
  111.  
  112. void        ZMenuBar::ClickMenuBar( const Point mousePt )
  113. {
  114.     long        mSelect;
  115.     
  116.     // initially disable all menu items
  117.     
  118.     DimMenus();
  119.     
  120.     // ask the command chain to reenable the relevant menu items
  121.  
  122.     if ( gCurHandler )
  123.         gCurHandler->UpdateMenus();
  124.  
  125.     // now track and select the menus
  126.     
  127.     mSelect = TrackMenuBar( mousePt );
  128.     
  129.     // dispatch the command
  130.     
  131.     DispatchCommand( mSelect );    
  132. }    
  133.  
  134.  
  135. /*-------------------------------***  TRACKMENUBAR  ***--------------------------------*/
  136. /*
  137. track the menubar, returning the item chosen.
  138. ---------------------------------------------------------------------------------------*/
  139.  
  140. long        ZMenuBar::TrackMenuBar( const Point mouse )    
  141. {
  142.     PauseCursorAnimation( 0 );
  143.  
  144.     return MenuSelect( mouse );
  145. }
  146.  
  147.  
  148. /*------------------------------***  UPDATEMENUBAR  ***--------------------------------*/
  149. /*
  150. redraw the menubar. If the menubar is currently dispatching to the command chain, set
  151. a flag in order to cause us to get redrawn when the command completes. If you don't do
  152. this, the menubar can get itself into a bad visual state.
  153. ---------------------------------------------------------------------------------------*/
  154.  
  155. void        ZMenuBar::UpdateMenuBar()
  156. {
  157.     if ( inDispatch || rbPending )
  158.         rbPending = TRUE;
  159.     else
  160.         DrawMenuBar();
  161. }
  162.  
  163.  
  164. /*------------------------------***  DISPATCHCOMMAND  ***------------------------------*/
  165. /*
  166. look up the command and send it up the chain
  167. ---------------------------------------------------------------------------------------*/
  168.  
  169. void    ZMenuBar::DispatchCommand( const long mSelect )
  170. {
  171.     MenuCmd        mCmd;
  172.     
  173.     // if an item was chosen, look up the command and send it up the chain
  174.     
  175.     mCmd.theCmd = noCommand;
  176.     
  177.     if ( HiWord( mSelect) != 0 && gCurHandler )
  178.     {
  179.         // set the zoom source rect in case the command spawns a window
  180.         #if _ZOOM_RECT_FX
  181.         
  182.         Rect    r;
  183.         
  184.         GetMenuTitleRect( HiWord( mSelect ), &r );
  185.         InsetRect( &r, 20, 4 );
  186.         SetGlobalZoomSource( &r );
  187.  
  188.         #endif
  189.         // if the nominated windows menu, handle the window selection
  190.         
  191.         if ( HiWord( mSelect ) == wmMenuID )
  192.             gWindowManager->SelectWindowFromMenu( LoWord( mSelect ));    
  193.  
  194.         FindMCmd( mSelect, &mCmd );
  195.         
  196.         if ( mCmd.theCmd != parentCmd )
  197.         {
  198.             // if the command was found, pass it up the chain. If not found, we still pass it
  199.             // up the chain, but this time as the direct menuID and itemID chosen. Commander
  200.             // classes can choose which of the two methods (or perhaps both?) to adopt.
  201.             
  202.             inDispatch = TRUE;
  203.             
  204.             if ( mCmd.theCmd != noCommand )
  205.                 gCurHandler->HandleCommand( mCmd.theCmd );
  206.             else
  207.                 gCurHandler->HandleCommand( HiWord( mSelect ), LoWord( mSelect ));    
  208.                 
  209.             inDispatch = FALSE;        
  210.         }
  211.  
  212.         SetTitleHilite( 0, FALSE );
  213.         
  214.         if ( rbPending )
  215.         {
  216.             rbPending = FALSE;
  217.             UpdateMenuBar();
  218.         }
  219.     }
  220. }    
  221.  
  222.  
  223. /*---------------------------------***  ENABLECMD  ***---------------------------------*/
  224. /*
  225. enable the menu item associated with the command
  226. ---------------------------------------------------------------------------------------*/
  227.  
  228. void        ZMenuBar::EnableCommand( const long cmd )
  229. {
  230.     short    m = 0, i = 0;
  231.     
  232.     FindCommand( cmd, &m, &i );
  233.     
  234.     if ( m && i )
  235.         EnableCommand( m, i );
  236. }    
  237.  
  238. /*---------------------------------***  ENABLECMD  ***---------------------------------*/
  239.  
  240. void        ZMenuBar::EnableCommand( const short menuID, const short itemID )
  241. {
  242.     MenuHandle    mH = FindMenuID( menuID );
  243.     
  244.     if ( mH )
  245.     {
  246.         EnableItem( mH, itemID );
  247.         
  248.         if ( itemID == 0 )
  249.             UpdateMenuBar();
  250.     }
  251. }    
  252.  
  253. /*---------------------------------***  DISABLECMD  ***--------------------------------*/
  254. /*
  255. disable the menu item associated with the command
  256. ---------------------------------------------------------------------------------------*/
  257.  
  258. void        ZMenuBar::DisableCommand( const long cmd )
  259. {
  260.     short    m = 0, i = 0;
  261.     
  262.     FindCommand( cmd, &m, &i );
  263.     
  264.     if ( m && i )
  265.         DisableCommand( m, i );
  266. }    
  267.  
  268. /*---------------------------------***  DISABLECMD  ***--------------------------------*/
  269.  
  270. void        ZMenuBar::DisableCommand( const short menuID, const short itemID )
  271. {
  272.     MenuHandle    mH = FindMenuID( menuID );
  273.     
  274.     if ( mH )
  275.     {
  276.         DisableItem( mH, itemID );
  277.         
  278.         if ( itemID == 0 )
  279.             UpdateMenuBar();
  280.     }
  281. }
  282.  
  283.  
  284. /*--------------------------------***  CHECKCOMMAND  ***-------------------------------*/
  285. /*
  286. check the menu command on or off.
  287. ---------------------------------------------------------------------------------------*/
  288.  
  289. void        ZMenuBar::CheckCommand( const long cmd, const Boolean checkOnOff )
  290. {
  291.     short    m = 0, i = 0;
  292.     
  293.     FindCommand( cmd, &m, &i );
  294.     
  295.     if ( m && i )
  296.         CheckCommand( m, i, checkOnOff );
  297. }
  298.  
  299.  
  300. /*--------------------------------***  CHECKCOMMAND  ***-------------------------------*/
  301.  
  302. void        ZMenuBar::CheckCommand( const short menuID, const short itemID, const Boolean checkOnOff )
  303. {
  304.     MenuHandle    mH = FindMenuID( menuID );
  305.     
  306.     if ( mH )
  307.         SetItemMark( mH, itemID, checkOnOff? menuCheckChar : noMark );
  308. }
  309.  
  310.  
  311. /*--------------------------------***  CHECKCOMMAND  ***-------------------------------*/
  312.  
  313. void        ZMenuBar::CheckCommand( const short menuID, Str255 itemString, const Boolean checkOnOff )
  314. {
  315.     // check the item with the text matching that passed (not case sensitive)
  316.     
  317.     MenuHandle    mH = FindMenuID( menuID );
  318.     Str255        iMatch;
  319.     
  320.     if ( mH )
  321.     {
  322.         short    m = CountMenuItems( mH );
  323.     
  324.         do
  325.         {
  326.             GetMenuItemText( mH, m, iMatch );
  327.             
  328.             if ( EqualString( itemString, iMatch, FALSE, TRUE ))
  329.             {
  330.                 SetItemMark( mH, m, checkOnOff? menuCheckChar : noMark );
  331.                 break;
  332.             }
  333.         }
  334.         while( --m );
  335.     }
  336. }
  337.  
  338.  
  339. /*----------------------------***  CHECKCOMMANDWITHCHAR  ***---------------------------*/
  340.  
  341. void        ZMenuBar::CheckCommandWithChar( const long cmd, const char checkChar )
  342. {
  343.     short    m = 0, i = 0;
  344.     
  345.     FindCommand( cmd, &m, &i );
  346.     
  347.     if ( m && i )
  348.     {
  349.         MenuHandle    mH = FindMenuID( m );
  350.         
  351.         if ( mH )
  352.             SetItemMark( mH, i, checkChar );
  353.     }
  354. }
  355.  
  356.  
  357. /*----------------------------***  CHECKCOMMANDWITHCHAR  ***---------------------------*/
  358.  
  359. void        ZMenuBar::CheckCommandWithChar( const short menuID, Str255 itemString, const char checkChar )
  360. {
  361.     MenuHandle    mH = FindMenuID( menuID );
  362.     Str255        iMatch;
  363.     
  364.     if ( mH )
  365.     {
  366.         short    m = CountMenuItems( mH );
  367.     
  368.         do
  369.         {
  370.             GetMenuItemText( mH, m, iMatch );
  371.             
  372.             if ( EqualString( itemString, iMatch, FALSE, TRUE ))
  373.             {
  374.                 SetItemMark( mH, m, checkChar );
  375.                 break;
  376.             }
  377.         }
  378.         while( --m );
  379.     }
  380. }
  381.  
  382.  
  383. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  384. /*
  385. set the text of a menu item to the desired string. Can be accessed by command or item.
  386. ---------------------------------------------------------------------------------------*/
  387.  
  388. void        ZMenuBar::SetCommandText( const long cmd, Str255 aText )
  389. {
  390.     short    m = 0, i = 0;
  391.     
  392.     FindCommand( cmd, &m, &i );
  393.     
  394.     if ( m && i )
  395.         SetCommandText( m, i, aText );
  396. }
  397.  
  398.  
  399. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  400.  
  401. void        ZMenuBar::SetCommandText( const short menuID, const short itemID, Str255 aText )
  402. {
  403.     MenuHandle    mH;
  404.     
  405.     mH = FindMenuID( menuID );
  406.     
  407.     if ( mH )
  408.         SetMenuItemText( mH, itemID, aText );
  409. }
  410.  
  411.  
  412. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  413.  
  414. void        ZMenuBar::SetCommandText( const long cmd, const short strListID, const short strIndex )
  415. {
  416.     Str255    aText;
  417.     
  418.     GetIndString( aText, strListID, strIndex );
  419.     if ( aText[0] > 0 )
  420.         SetCommandText( cmd, aText );
  421. }
  422.  
  423.  
  424. /*-------------------------------***  SETCOMMANDTEXT  ***------------------------------*/
  425.  
  426. void        ZMenuBar::SetCommandText( const short menuID, const short itemID, const short strListID, const short strIndex )
  427. {
  428.     Str255    aText;
  429.     
  430.     GetIndString( aText, strListID, strIndex );
  431.     if ( aText[0] > 0 )
  432.         SetCommandText( menuID, itemID, aText );
  433. }
  434.  
  435. /*----------------------------***  SETCOMMANDTEXTSTYLE  ***---------------------------*/
  436.  
  437. void        ZMenuBar::SetCommandTextStyle( const long cmd, Style aStyle )
  438. {
  439.     short        m = 0, i = 0;
  440.     MenuHandle    mh;
  441.     
  442.     FindCommand( cmd, &m, &i );
  443.     
  444.     if ( m && i )
  445.     {
  446.         mh = FindMenuID( m );
  447.         
  448.         if ( mh )
  449.             SetItemStyle( mh, i, aStyle );
  450.     }
  451. }
  452.  
  453.  
  454. /*-------------------------------***  SHOWHIDEMENUBAR  ***-----------------------------*/
  455. /*
  456. Show or hide the menubar. This can be used in two ways. You can set the menubar hidden
  457. completely by passing MBAR_HIDE, and reshow it with MBAR_SHOW. OR you can pass in
  458. MBAR_HIDE_MOUSEAWARE and a global mouse location to show or hide the bar dynamically as
  459. the user moves the mouse. ZEventHandler will provide this functionality if you define
  460. _AUTO_MBAR_HIDING.
  461. ---------------------------------------------------------------------------------------*/
  462.  
  463. void        ZMenuBar::ShowHideMenuBar( MBarHiding    mHiding, Point gMouseLoc )
  464. {
  465.     Boolean        showIt;
  466.     Rect        msRect;
  467.     GDHandle    theDevice;
  468.     RgnHandle    temp;
  469.     
  470.     if ( mHiding != mbHiding )
  471.     {
  472.         // state has changed, or we want to determine it from mouse position
  473.     
  474.         theDevice = GetMainDevice();
  475.         msRect = (*theDevice)->gdRect;
  476.         msRect.bottom = msRect.top + mBarHeight;
  477.             
  478.         if ( mHiding == MBAR_HIDE_MOUSEAWARE )
  479.         {
  480.             showIt = PtInRect( gMouseLoc, &msRect );
  481.             
  482.             // if menubar already in indicated state, do nothing
  483.             
  484.             if (( showIt  && ( mbHiding == MBAR_SHOW )) ||
  485.                 ( !showIt && ( mbHiding == MBAR_HIDE )))
  486.                 return;
  487.         }
  488.         else
  489.             showIt = ( mHiding == MBAR_SHOW );
  490.             
  491.         FailNIL( temp = NewRgn());
  492.         RectRgn( temp, &msRect );
  493.  
  494.         if ( showIt )
  495.         {
  496.             LMSetMBarHeight( mBarHeight );
  497.             DiffRgn(LMGetGrayRgn(), temp, LMGetGrayRgn());
  498.             UpdateMenuBar();
  499.             
  500.             mbHiding = MBAR_SHOW;
  501.         }
  502.         else
  503.         {
  504.             LMSetMBarHeight( 0 );
  505.             UnionRgn(LMGetGrayRgn(), temp, LMGetGrayRgn());
  506.             
  507.             mbHiding = MBAR_HIDE;
  508.         }
  509.         
  510.         // calculate and refresh vis regions of windows
  511.         
  512.         WindowPtr    fw = FrontWindow();
  513.         
  514.         if ( fw )
  515.         {
  516.             PaintBehind( fw, temp );
  517.             CalcVisBehind( fw,  temp );
  518.         }
  519.         DisposeRgn( temp );
  520.     }
  521. }
  522.  
  523. /*-------------------------------***  SHOWHIDEMENUBAR  ***-----------------------------*/
  524.  
  525.  
  526. void    ZMenuBar::ShowHideMenuBar( MBarHiding mHiding )
  527. {
  528.     Point unused = { 0, 0 };
  529.     
  530.     ShowHideMenuBar( mHiding, unused );
  531. }
  532.  
  533.  
  534. /*-----------------------------***  NOMINATEWINDOWSMENU  ***---------------------------*/
  535. /*
  536. This method can be used to set up an automatic "Windows" menu. To do this, call this
  537. method once your bar is built. This will nominate the menu with the ID passed as the
  538. Windows menu. Any items in the menu already will be retained and work as normal. As
  539. windows are added and deleted in the window manager, this will maintain the menu for you.
  540. ---------------------------------------------------------------------------------------*/
  541.  
  542. void        ZMenuBar::NominateWindowsMenu( const short menuID )    
  543. {
  544.     if ( wmMenuID == 0 )
  545.     {
  546.         MenuHandle    mH = FindMenuID( menuID );
  547.         
  548.         if ( mH )
  549.         {
  550.             wmMenuID = menuID;
  551.             
  552.             SetMenuDimming( menuID, disableCmdsOnly );
  553.             
  554.             // we need to work hand-in-hand with the window manager to
  555.             // keep track of the windows. The Window Manager is best placed to
  556.             // do all this, so we simply hand off the menu to it.
  557.             
  558.             gWindowManager->SetWindowsMenu( mH );
  559.         }
  560.     }
  561. }
  562.  
  563.  
  564. /*-------------------------------***  INSERTHELPITEM  ***------------------------------*/
  565. /*
  566. append the text to the help menu, and return the item number of the resulting item. To
  567. process this item, override HandleCommand and look for kHMHelpMenuID and the item returned
  568. from this method.
  569. ---------------------------------------------------------------------------------------*/
  570.  
  571. short        ZMenuBar::AppendHelpItem( Str255 itemText )        
  572. {
  573.     short        i;
  574.     MenuHandle    helpMenuH;
  575.     
  576.     FailOSErr( HMGetHelpMenuHandle( &helpMenuH ));
  577.     
  578.     AppendMenu( helpMenuH, itemText );    
  579.     
  580.     i = CountMItems( helpMenuH );
  581.  
  582.     return i;
  583. }
  584.  
  585.  
  586. /*------------------------------***  APPENDMENUTOBAR  ***------------------------------*/
  587. /*
  588. This can be called to add menus "on the fly" into the bar. This reads a menu from a
  589. MENU or CMNU resource, parses its comands and inserts the menu. Menus can only be added
  590. to the end of the bar. You need to call UpdateMenuBar after this or a series of these.
  591. ---------------------------------------------------------------------------------------*/
  592.  
  593. void        ZMenuBar::AppendMenuToBar( const short menuID  )
  594. {
  595.     Handle    temp;
  596.     
  597.     temp = GetResource( 'CMNU', menuID );
  598.     
  599.     if ( temp )
  600.     {
  601.         LoadCMNUMenu( menuID );
  602.         ReleaseResource( temp );
  603.     }
  604.     else
  605.         LoadMenu( menuID );
  606.     
  607.     mbCount++;    
  608. }
  609.  
  610. /*-----------------------------***  REMOVEMENUFROMBAR  ***-----------------------------*/
  611. /*
  612. This can be called to remove a menu added with the above routine. You need to call
  613. UpdateMenuBar after this or a series of these.
  614. ---------------------------------------------------------------------------------------*/
  615.  
  616. void        ZMenuBar::RemoveMenuFromBar( const short menuID )
  617. {
  618.     MenuHandle    mH;
  619.     
  620.     FailNILParam( mH = GetMenuHandle( menuID ));    
  621.     
  622.     // remove the command entries for this menu and any submenus it is the parent of.
  623.     
  624.     UnloadMenu( mH );
  625.     mbCount--;
  626. }
  627.  
  628.  
  629. /*------------------------------***  UPDATESTYLEMENU  ***------------------------------*/
  630. /*
  631. sets checkmarks agains standard syle command according to style info passed
  632. ---------------------------------------------------------------------------------------*/
  633.  
  634. void        ZMenuBar::UpdateStyleMenu( TEStyleRunInfo* runInfo )
  635. {
  636.     EnableCommand( kCmdPlainText );
  637.     EnableCommand( kCmdBoldText );
  638.     EnableCommand( kCmdItalicText );
  639.     EnableCommand( kCmdUnderlineText );
  640.     EnableCommand( kCmdOutlineText );
  641.     EnableCommand( kCmdShadowText );
  642.     EnableCommand( kCmdCondensedText );
  643.     EnableCommand( kCmdExtendedText );
  644.     
  645.     Style    curStyle = runInfo->runStyles;
  646.     Style    cs = runInfo->contStyles;
  647.     Boolean continuousRun = ( cs == 0xFF );
  648.  
  649.     if (( curStyle & 0x7F ) == 0 )
  650.         CheckCommand( kCmdPlainText, TRUE );
  651.     else
  652.     {
  653.         if ( curStyle & kPlainStyle )
  654.             CheckCommandWithChar( kCmdPlainText, '-' );
  655.         
  656.         if ( curStyle & bold )
  657.             CheckCommandWithChar( kCmdBoldText, ( cs & bold ) == bold? checkMark : '-' );
  658.  
  659.         if ( curStyle & italic )
  660.             CheckCommandWithChar( kCmdItalicText, ( cs & italic ) == italic? checkMark : '-' );
  661.         
  662.         if ( curStyle & underline )
  663.             CheckCommandWithChar( kCmdUnderlineText, ( cs & underline ) == underline? checkMark : '-' );
  664.         
  665.         if ( curStyle & outline )
  666.             CheckCommandWithChar( kCmdOutlineText, ( cs & outline ) == outline? checkMark : '-' );
  667.         
  668.         if ( curStyle & shadow )
  669.             CheckCommandWithChar( kCmdShadowText, ( cs & shadow ) == shadow? checkMark : '-' );
  670.         
  671.         if ( curStyle & condense )
  672.             CheckCommandWithChar( kCmdCondensedText, ( cs & condense ) == condense? checkMark : '-' );
  673.         
  674.         if ( curStyle & extend )
  675.             CheckCommandWithChar( kCmdExtendedText, ( cs & extend ) == extend? checkMark : '-' );
  676.     }
  677. }
  678.  
  679. /*------------------------------***  UPDATESTYLEMENU  ***------------------------------*/
  680.  
  681.  
  682. void        ZMenuBar::UpdateStyleMenu( Style aStyle )
  683. {
  684.     EnableCommand( kCmdPlainText );
  685.     EnableCommand( kCmdBoldText );
  686.     EnableCommand( kCmdItalicText );
  687.     EnableCommand( kCmdUnderlineText );
  688.     EnableCommand( kCmdOutlineText );
  689.     EnableCommand( kCmdShadowText );
  690.     EnableCommand( kCmdCondensedText );
  691.     EnableCommand( kCmdExtendedText );
  692.  
  693.     if ( aStyle == normal )
  694.         CheckCommand( kCmdPlainText, TRUE );
  695.     else
  696.     {
  697.         CheckCommand( kCmdBoldText,      ( aStyle & bold )         == bold );
  698.         CheckCommand( kCmdItalicText,      ( aStyle & italic )     == italic );
  699.         CheckCommand( kCmdUnderlineText, ( aStyle & underline ) == underline );
  700.         CheckCommand( kCmdOutlineText,      ( aStyle & outline )     == outline );
  701.         CheckCommand( kCmdShadowText,      ( aStyle & shadow )     == shadow );
  702.         CheckCommand( kCmdCondensedText, ( aStyle & condense )     == condense );
  703.         CheckCommand( kCmdExtendedText,  ( aStyle & extend )    == extend );
  704.     }
  705. }
  706.  
  707. /*----------------------------***  UPDATEFONTSIZEMENU  ***-----------------------------*/
  708. /*
  709. sets checkmark against current font size, plus sets outline style for real font sizes
  710. in the current font. This iterates through the possible commands so it will work no
  711. matter where you have decided to place your size commands. Neat.
  712. ---------------------------------------------------------------------------------------*/
  713.  
  714. void        ZMenuBar::UpdateFontSizeMenu( TEStyleRunInfo* runInfo )
  715. {
  716.     Boolean    contFont, contSize;
  717.     
  718.     contFont = runInfo->fr & kFontIsContinuous;
  719.     contSize = runInfo->fr & kSizeIsContinuous;
  720.     
  721.     long    fCmd;
  722.     short     i;
  723.     Str255    fontName;
  724.     
  725.     for ( fCmd = kCmdStdFontSize7; fCmd <= kCmdStdFontSize72; fCmd++ )
  726.     {
  727.         EnableCommand( fCmd );
  728.         
  729.         if ( contFont && RealFont( runInfo->fonts[0], fCmd - kStdFontSizeBase ))
  730.             SetCommandTextStyle( fCmd, outline );
  731.         else
  732.             SetCommandTextStyle( fCmd, 0 );
  733.     }
  734.  
  735.     if ( contSize )
  736.         CheckCommand( kStdFontSizeBase + runInfo->sizes[0], TRUE );
  737.     else
  738.     {
  739.         // non-continuous sizes, so mark all the listed sizes
  740.         
  741.         for ( i = 0; i < runInfo->numSizes; i++ )
  742.             CheckCommandWithChar( runInfo->sizes[i] + kStdFontSizeBase, '-' );    
  743.     }
  744.     
  745.     if ( gFontMenuID )
  746.     {
  747.         if ( contFont )
  748.         {
  749.             GetFontName( runInfo->fonts[0], fontName );
  750.             CheckCommand( gFontMenuID, fontName, TRUE );
  751.         }
  752.         else
  753.         {
  754.             // non-continous fonts, so mark all the listed fonts
  755.             
  756.             for ( i = 0; i < runInfo->numFonts; i++ )
  757.             {
  758.                 GetFontName( runInfo->fonts[i], fontName );
  759.                 CheckCommandWithChar( gFontMenuID, fontName, '-' );
  760.             }
  761.         }    
  762.     }
  763. }
  764.  
  765.  
  766. /*---------------------------------***  LOADMENUS  ***---------------------------------*/
  767. /*
  768. for each menu in the MBAR, call LoadMenu. This will deal with hierarchical menus, etc.
  769. This method assumes mBarH is valid, and won't be purged.
  770. ---------------------------------------------------------------------------------------*/
  771.  
  772. void        ZMenuBar::LoadMenus( const Boolean autoInstall )
  773. {
  774.     short        i, menuID;
  775.     Handle        temp;
  776.     
  777.     // how many menus in MBAR resource? This is first item in resource.
  778.     
  779.     mbCount = (*mBarH)[0];
  780.     
  781.     // iterate through, looking for MENU or CMNU resources
  782.     
  783.     for ( i = 1; i <= mbCount; i++ )
  784.     {
  785.         menuID = (*mBarH)[i];
  786.         
  787.         // if CMNU resource is available, use that. Otherwise, use MENU resource,
  788.         // possibly parsing it for command numbers
  789.         
  790.         temp = GetResource( 'CMNU', menuID );
  791.         
  792.         if ( temp )
  793.         {
  794.             LoadCMNUMenu( menuID, FALSE, autoInstall );
  795.             ReleaseResource( temp );
  796.         }
  797.         else
  798.             LoadMenu( menuID, FALSE, autoInstall );
  799.  
  800.     }
  801. }    
  802.  
  803. /*----------------------------------***  DIMMENUS  ***---------------------------------*/
  804. /*
  805. dim all of the menu items, according to their flags.
  806. ---------------------------------------------------------------------------------------*/
  807.  
  808. void        ZMenuBar::DimMenus()
  809. {
  810.     // This originally operated by walking the low-memory global menuList, but that does
  811.     // not work when 3rd party extensions install extra menus. Thus we now use our private
  812.     // list of menus to do this operation so we only affect our own menus.
  813.     
  814.     short        i;
  815.     MenuInfRec    mRec;
  816.     
  817.     for ( i = 1; i <= theMenus->CountItems(); i++ )
  818.     {
  819.         // find this menu in our list, and dim it according to the
  820.         // flags there. (Thanks to Jean-Yves Pochez for the improvements to this method)
  821.         
  822.         mRec.macMenu = NULL;
  823.         theMenus->GetArrayItem( &mRec, i );
  824.     
  825.         if ( mRec.macMenu )
  826.         {
  827.             // menu is in our list, so dim it. Note that all menus, including all of
  828.             // the attached hierarchical menus are dimmed by iterating this list. Thus
  829.             // PredimMenu does not need to be recursive, and isn't.
  830.             
  831.             PredimMenu( mRec.macMenu );
  832.         }
  833.     }
  834.  
  835.     // if auto Windows menu in use, set that up as well
  836.     
  837.     if ( wmMenuID > 0 )
  838.         gWindowManager->BuildWindowsMenu();    
  839. }
  840.  
  841.  
  842. /*-------------------------------***  SETMENUDIMMING  ***------------------------------*/
  843. /*
  844. set the dimming options for a particular menu. By default, all menus are dimmed auto-
  845. matically, but if you don't want them dimmed, or partially dimmed, call this with the
  846. required options.
  847. ---------------------------------------------------------------------------------------*/
  848.  
  849. void        ZMenuBar::SetMenuDimming( const short menuID, const DimmingOptions dimOpts )
  850. {
  851.     MenuInfRec    mr;
  852.     Boolean        bUpdate;
  853.     
  854.     mr.macMenu = NULL;
  855.     
  856.     FindMenuInfo( menuID, &mr );
  857.     
  858.     if ( mr.macMenu )
  859.     {
  860.         // if this call will change the state of the dimTitle flag, we need to cause an
  861.         // update to the menubar so that the user sees the change
  862.         
  863.         bUpdate = (( mr.mDimming ^ dimOpts ) & dimTitle ) != 0;
  864.         
  865.         mr.mDimming = dimOpts;
  866.         theMenus->SetArrayItem( &mr, mr.mIndex );
  867.         
  868.         // update the bar if necessary
  869.         
  870.         if ( bUpdate )
  871.         {
  872.             // enable or disable title item
  873.             
  874.             if ( dimOpts & dimTitle )
  875.                 (*mr.macMenu)->enableFlags &= 0xFFFFFFFE;
  876.             else
  877.                 (*mr.macMenu)->enableFlags |= 1;
  878.             
  879.             UpdateMenuBar();
  880.         }
  881.     }
  882. }    
  883.  
  884.  
  885. /*----------------------------------***  LOADMENU  ***---------------------------------*/
  886. /*
  887. can be called recursively- try to load the menu with the ID and any submenus it refers
  888. to. If MENU resources are not found, try CMNU resources.
  889. ---------------------------------------------------------------------------------------*/
  890.  
  891. void        ZMenuBar::LoadMenu( const short menuID, Boolean isHMenu, Boolean autoInstall )
  892. {
  893.     short        i, mCount, cmdChar, subID;
  894.     MenuHandle    mH;
  895.     MenuCmd        mCmd;
  896.     MenuInfRec    mRec;
  897.     Str255        iText;
  898.     
  899.     FailNILRes( mH = GetMenu( menuID ));
  900.     
  901.     // set up info record for this menu
  902.     
  903.     mRec.menuID = menuID;
  904.     mRec.mIndex = miSeed++;
  905.     mRec.macMenu = mH;
  906.     mRec.mDimming = dimCommands;
  907.     mRec.mIsResource = TRUE;
  908.     
  909.     theMenus->AppendItem( &mRec );
  910.     
  911.     // look through the menu for submenus, and recursively add them
  912.     
  913.     mCount = CountMenuItems( mH );
  914.     
  915.     for( i = 1; i <= mCount; i++ )
  916.     {
  917.         GetMenuItemText( mH, i, iText );
  918.         
  919.         if ( iText[1] != '-' )
  920.         {
  921.             // make a command entry for the item
  922.             
  923.             mCmd.menuID = menuID;
  924.             mCmd.itemID = i;
  925.             mCmd.macMenu = mH;
  926.             
  927.             ParseMenuItem( iText, &mCmd.theCmd );
  928.             
  929.             // menus created using GetMenu are resources, CMNU are not. This is flagged so
  930.             // we can dispose of it correctly.
  931.             
  932.             mCmd.cmdFlags = autoUnCheck | ( isHMenu? 0 : isPrimaryMenu ) | menuIsResource;
  933.             mCmd.subMenuID = 0;
  934.             
  935.             SetMenuItemText( mH, i, iText );
  936.             GetItemCmd( mH, i, &cmdChar );
  937.             
  938.             if ( cmdChar == hMenuCmd )
  939.             {
  940.                 // has a submenu, so load it:
  941.                 
  942.                 GetItemMark( mH, i, &subID );
  943.                 
  944.                 // need to determine if submenu is a MENU or CMNU resource:
  945.                 
  946.                 Handle    cmnuH;
  947.                 
  948.                 cmnuH = GetResource( 'CMNU', subID );
  949.                 
  950.                 if ( cmnuH )
  951.                 {
  952.                     LoadCMNUMenu( subID, TRUE, autoInstall );
  953.                     ReleaseResource( cmnuH );
  954.                 }
  955.                 else
  956.                     LoadMenu( subID, TRUE, autoInstall );
  957.                     
  958.                 mCmd.subMenuID = subID;
  959.                 mCmd.theCmd = parentCmd;
  960.             }
  961.             
  962.             theMenuCmds->AppendItem( &mCmd );
  963.         }
  964.     }
  965.     
  966.     // add the menu to the system list
  967.     
  968.     if ( autoInstall )
  969.         InsertMenu( mH, isHMenu? hierMenu : 0 );
  970. }    
  971.  
  972. /*--------------------------------***  LOADCMNUMENU  ***-------------------------------*/
  973. /*
  974. ditto for CMNU resources- this can parse that format of resource.
  975. ---------------------------------------------------------------------------------------*/
  976.  
  977. void        ZMenuBar::LoadCMNUMenu( const short menuID, Boolean isHMenu, Boolean autoInstall )
  978. {
  979.     CMNUResHdl    cH;
  980.     MenuCmd        mCmd;
  981.     MenuInfRec    mRec;
  982.     MenuHandle    mH;
  983.     Ptr            cmP, cmItemText;
  984.     short        itemID;
  985.     
  986.     cH = ( CMNUResHdl ) GetResource( 'CMNU', menuID );
  987.     
  988.     if ( cH )
  989.     {
  990.         // we need to do two things with this menu- a) make a normal menu handle that
  991.         // can be inserted into the menu manager list, and b) a set of menuCmd records
  992.         // so that we can look up the command when an item is chosen.
  993.     
  994.         FailNIL( mH = NewMenu( menuID, (ConstStr255Param) &(*cH)->mTitle ));
  995.         
  996.         // set up info record for this menu
  997.         
  998.         mRec.menuID = menuID;
  999.         mRec.mIndex = miSeed++;
  1000.         mRec.macMenu = mH;
  1001.         mRec.mDimming = dimCommands;
  1002.         mRec.mIsResource = FALSE;
  1003.         
  1004.         theMenus->AppendItem( &mRec );
  1005.         
  1006.         HLock((Handle) cH );
  1007.         
  1008.         // we need to iterate through the items in the CMNU and build our two structures
  1009.         // as needed. To do this efficiently, we lock the handle and keep a running pointer.
  1010.         
  1011.         cmItemText = cmP = &(*cH)->mTitle + (*cH)->mTitle + 1;
  1012.         itemID = 0;
  1013.         
  1014.         // we're now at the start of the first item's text. We now scan through each item
  1015.         // building both the real menu and the command structure
  1016.         
  1017.         while( *cmItemText != 0 )
  1018.         {
  1019.             itemID++;
  1020.             
  1021.             cmP += *cmItemText + 1;        // point to top of "interesting" info
  1022.             
  1023.             // add text of item, making sure meta-characters are ignored. Note that
  1024.             // a dividing line still works correctly
  1025.             
  1026.             AppendMenu( mH, "\px" );
  1027.             SetMenuItemText( mH, itemID, (ConstStr255Param) cmItemText );
  1028.             
  1029.             // add command entry if not a dividing line
  1030.             
  1031.             if ( cmItemText[1] != '-' )
  1032.             {
  1033.                 // make menu item match info
  1034.                 
  1035.                 SetItemIcon    ( mH, itemID, ((CMNUEntryPtr) cmP)->iconID );
  1036.                 SetItemCmd    ( mH, itemID, ((CMNUEntryPtr) cmP)->keyEqu );
  1037.                 SetItemMark    ( mH, itemID, ((CMNUEntryPtr) cmP)->markChar );
  1038.                 SetItemStyle( mH, itemID, ((CMNUEntryPtr) cmP)->iStyle );
  1039.                 
  1040.                 mCmd.menuID = menuID;
  1041.                 mCmd.itemID = itemID;
  1042.                 mCmd.subMenuID = 0;
  1043.                 mCmd.macMenu = mH;
  1044.                 mCmd.cmdFlags = autoUnCheck | ( isHMenu? 0 : isPrimaryMenu );
  1045.                 mCmd.theCmd = noCommand;
  1046.                 
  1047.                 // if there's a sub-menu, we'll need to find and load it too. This involves
  1048.                 // a recursion to this function or to LoadMenu.
  1049.                 
  1050.                 if (((CMNUEntryPtr) cmP)->keyEqu == hMenuCmd )
  1051.                 {
  1052.                     mCmd.subMenuID = ((CMNUEntryPtr) cmP)->markChar;
  1053.                 
  1054.                     // is this a CMNU or a MENU resource?
  1055.                     
  1056.                     Handle    cmnuH = GetResource( 'CMNU', mCmd.subMenuID );
  1057.                     
  1058.                     if ( cmnuH )
  1059.                     {
  1060.                         LoadCMNUMenu( mCmd.subMenuID, TRUE, autoInstall );
  1061.                         ReleaseResource( cmnuH );
  1062.                     }
  1063.                     else
  1064.                         LoadMenu( mCmd.subMenuID, TRUE, autoInstall );
  1065.                         
  1066.                     mCmd.theCmd = parentCmd;
  1067.                 }
  1068.                 else
  1069.                 {
  1070.                     // set up the command entry for the item. The command number
  1071.                     // may follow a pad byte if it would otherwise be at an odd address, so the
  1072.                     // data lies 4 or 5 bytes away from where cmP is now:
  1073.                 
  1074.                     if ((unsigned long) cmP & 1 )
  1075.                         mCmd.theCmd = *(long*)( cmP + sizeof( CMNUEntry ) + 1 );
  1076.                     else
  1077.                         mCmd.theCmd = *(long*)( cmP + sizeof( CMNUEntry ));
  1078.                 }
  1079.                 // add the item to our command array
  1080.                 
  1081.                 theMenuCmds->AppendItem( &mCmd );
  1082.             }    
  1083.             
  1084.             // set the pointers to the next item. This is 8 or 9 bytes away depending on
  1085.             // whether the resulting address is odd or even
  1086.             
  1087.             cmP += 8;
  1088.             if ((unsigned long) cmP & 1 )
  1089.                 cmP++;
  1090.                 
  1091.             cmItemText = cmP;
  1092.         }
  1093.  
  1094.         HUnlock((Handle) cH );
  1095.         
  1096.         // insert the menu we just built into the system list
  1097.         
  1098.         if ( autoInstall )
  1099.             InsertMenu( mH, isHMenu? hierMenu : 0 );
  1100.     }
  1101. }
  1102.  
  1103.  
  1104. /*---------------------------------***  UNLOADMENU  ***--------------------------------*/
  1105. /*
  1106. removes this menu from the command list and un-inserts itself. It also calls itself to
  1107. deal with its submenus. Called by RemoveMenuFromBar. Use with care.
  1108. ---------------------------------------------------------------------------------------*/
  1109.  
  1110. void        ZMenuBar::UnloadMenu( MenuHandle mH )
  1111. {
  1112.     long        m;
  1113.     MenuCmd        mCmd;
  1114.     Boolean        resStatus = FALSE;
  1115.     Boolean        isResMenu = FALSE;
  1116.  
  1117.     for( m = theMenuCmds->CountItems(); m > 0; m-- )
  1118.     {
  1119.         theMenuCmds->GetArrayItem( &mCmd, m );
  1120.         
  1121.         // if this is one pertaining to the menu, delete it from the array. Also
  1122.         // if its a parent item, call this again to delete the submenu too.
  1123.     
  1124.         if ( mH == mCmd.macMenu )
  1125.         {
  1126.             theMenuCmds->DeleteItem( m );
  1127.         
  1128.             // is this a parent?
  1129.             
  1130.             if ( mCmd.theCmd == parentCmd &&
  1131.                  mCmd.subMenuID != 0 )
  1132.             {
  1133.                 // yes, so recurse and delete that too
  1134.                 
  1135.                 MenuHandle    smH = GetMenuHandle( mCmd.subMenuID );
  1136.                 
  1137.                 if ( smH )
  1138.                     UnloadMenu( smH );
  1139.             }
  1140.             
  1141.             if ( !resStatus )
  1142.             {
  1143.                 isResMenu = ( mCmd.cmdFlags & menuIsResource ) == menuIsResource;
  1144.                 resStatus = TRUE;
  1145.             }
  1146.         }
  1147.     }    
  1148.     // remove the menu from the system menu list
  1149.     
  1150.     DeleteMenu((*mH)->menuID );
  1151.     
  1152.     // dispose or release the menu handle according to whether
  1153.     // it was a resource or not
  1154.     
  1155.     if ( isResMenu )
  1156.         ReleaseResource((Handle) mH );
  1157.     else
  1158.         DisposeMenu( mH );
  1159. }    
  1160.  
  1161. /*---------------------------------***  PREDIMMENU  ***--------------------------------*/
  1162. /*
  1163. can be called recursively- dim this menu and any submenus it owns.
  1164. ---------------------------------------------------------------------------------------*/
  1165.  
  1166. void        ZMenuBar::PredimMenu( MenuHandle theMenu )
  1167. {
  1168.     short            m, i, cmd;
  1169.     MenuInfRec        mi;
  1170.     
  1171.     if ( theMenu )
  1172.     {
  1173.         // initially set all items to be dimmed except the title ( we make one
  1174.         // exception- the apple menu only has the first item dimmed );
  1175.         
  1176.         if ((*theMenu)->menuID == kAppleMenuID )
  1177.             (*theMenu)->enableFlags = 0xFFFFFFF9;
  1178.         else
  1179.         {
  1180.             // get the info rec for this menu
  1181.             
  1182.             FindMenuInfo((*theMenu)->menuID, &mi );
  1183.             
  1184.             // dim items according to dimming flags except title
  1185.             // if title already dimmed, it is not re-enabled here.
  1186.             
  1187.             if (( mi.mDimming & 0x0F ) != neverDim )
  1188.                 (*theMenu)->enableFlags &= 0x00000001;
  1189.         
  1190.             // look through the items for submenus
  1191.             
  1192.             m = CountMenuItems( theMenu );
  1193.         
  1194.             for( i = 1; i <= m; i++ )
  1195.             {
  1196.                 GetItemCmd( theMenu, i, &cmd );
  1197.                 
  1198.                 if ( cmd == hMenuCmd )
  1199.                 {
  1200.                     // Enable the parent item if permitted
  1201.                     
  1202.                     if (( mi.mDimming & dimParentItems ) == 0 )
  1203.                         EnableItem( theMenu, i );
  1204.                 }
  1205.                 else
  1206.                 {
  1207.                     // if the item has no command and we don't want these dimming, enable it
  1208.                     
  1209.                     if ( mi.mDimming & dimCommands )
  1210.                     {
  1211.                         MenuCmd        mc;
  1212.                         
  1213.                         mc.theCmd = 0;
  1214.                         FindMCmd((((long)(*theMenu)->menuID ) << 16 ) | i, &mc );
  1215.                         
  1216.                         if ( mc.theCmd == 0 )
  1217.                             EnableItem( theMenu, i );
  1218.                     }
  1219.                     // in case the item is checked, uncheck it
  1220.                     
  1221.                     SetItemMark( theMenu, i, noMark );
  1222.                 }
  1223.             }
  1224.         }
  1225.     }    
  1226. }    
  1227.  
  1228. /*-------------------------------***  PARSEMENUITEM  ***-------------------------------*/
  1229. /*
  1230. extract command info from the item for TCL-style menu items
  1231. ---------------------------------------------------------------------------------------*/
  1232.  
  1233. void        ZMenuBar::ParseMenuItem( Str255 iText, long* aCmd )
  1234. {
  1235.     *aCmd = noCommand;
  1236.     
  1237.     // a valid command is associated with the item by appending a hash sysmbol (#),
  1238.     // followed by the command number as a string. Here we extract the number and
  1239.     // modify the string to exclude it.
  1240.     
  1241.     unsigned char    i = 1;
  1242.     Str15            subStr;
  1243.     
  1244.     // search for # char
  1245.     
  1246.     while((iText[i] != '#') && (i < iText[0]))
  1247.         i++;
  1248.     
  1249.     if ( i < iText[0] )
  1250.     {
  1251.         // extract substring which is command number in string form
  1252.         
  1253.         BlockMoveData( &iText[i + 1], &subStr[1], iText[0] - i );
  1254.         subStr[0] = iText[0] - i;
  1255.         
  1256.         StringToNum( subStr, aCmd );
  1257.         
  1258.         // truncate string
  1259.         
  1260.         iText[0] = i - 1;
  1261.     }
  1262. }    
  1263.  
  1264. /*------------------------------***  APPENDSTDITEMS  ***-------------------------------*/
  1265. /*
  1266. appends DA names or FONT names to the menu
  1267. ---------------------------------------------------------------------------------------*/
  1268.  
  1269. void        ZMenuBar::AppendStdItems( const short menuID, const short iType )
  1270. {
  1271.     MenuHandle    mH;
  1272.     
  1273.     mH = FindMenuID( menuID );
  1274.     
  1275.     if ( mH )
  1276.         AppendResMenu( mH, ( iType == appendDANames )? 'DRVR' : 'FONT' );
  1277.     
  1278.     // if we are creating a font menu, record the menuID in a global so that classes that
  1279.     // want to get info from the font menu can do so simply. ZTextWindow uses this, for example.
  1280.     
  1281.     if ( mH && ( iType == appendFontNames ))
  1282.     {
  1283.         gFontMenuID = menuID;
  1284.         SetMenuDimming( menuID, neverDim + dimTitle );
  1285.     }
  1286. }    
  1287.  
  1288.  
  1289. /*----------------------------------***  FINDMCMD  ***---------------------------------*/
  1290. /*
  1291. find the command entry associated with the mSelection value.
  1292. ---------------------------------------------------------------------------------------*/
  1293.  
  1294. void        ZMenuBar::FindMCmd( const long mSelect, MenuCmd* aCmd )
  1295. {
  1296.     long        m;
  1297.     MenuCmd        mCmd;
  1298.     
  1299.     for( m = 1; m <= theMenuCmds->CountItems(); m++ )
  1300.     {
  1301.         theMenuCmds->GetArrayItem( &mCmd, m );
  1302.     
  1303.         // is this the one we want?
  1304.         
  1305.         if ( HiWord( mSelect ) == mCmd.menuID &&
  1306.              LoWord( mSelect ) == mCmd.itemID )
  1307.         {
  1308.             *aCmd = mCmd;
  1309.             break;
  1310.         }
  1311.     }    
  1312. }    
  1313.  
  1314.  
  1315. /*--------------------------------***  FINDCOMMAND  ***--------------------------------*/
  1316. /*
  1317. inverse operation- given a command, return the original menu item and ID.
  1318. ---------------------------------------------------------------------------------------*/
  1319.  
  1320. void        ZMenuBar::FindCommand( const long cmd, short* menuID, short* itemID )    
  1321. {
  1322.     long        m;
  1323.     MenuCmd        mCmd;
  1324.     
  1325.     for( m = 1; m <= theMenuCmds->CountItems(); m++ )
  1326.     {
  1327.         theMenuCmds->GetArrayItem( &mCmd, m );
  1328.     
  1329.         // is this the one we want?
  1330.         
  1331.         if ( cmd == mCmd.theCmd )
  1332.         {
  1333.             *menuID = mCmd.menuID;
  1334.             *itemID = mCmd.itemID;
  1335.             break;
  1336.         }
  1337.     }    
  1338. }
  1339.  
  1340.  
  1341. /*------------------------------***  SETTITLEHILITE  ***-------------------------------*/
  1342. /*
  1343. set the menu title hilite on or off
  1344. ---------------------------------------------------------------------------------------*/
  1345.  
  1346. void        ZMenuBar::SetTitleHilite( const short menuID, const Boolean state )
  1347. {
  1348.     if ( state )
  1349.         HiliteMenu( menuID );
  1350.     else
  1351.         HiliteMenu( 0 );
  1352. }
  1353.  
  1354.  
  1355. /*--------------------------------***  FINDMENUID  ***---------------------------------*/
  1356. /*
  1357. returns the handle of the menu with the given ID, even if it has not been inserted into
  1358. the system list. This can only find menus that belong to this bar object however.
  1359. ---------------------------------------------------------------------------------------*/
  1360.  
  1361. MenuHandle    ZMenuBar::FindMenuID( const short menuID )
  1362. {
  1363.     // returns the handle of the menu with the given ID. This works whether or not the menu
  1364.     // was actually installed in the system menu list. If so, we use the toolbox call. If not
  1365.     // we use the slightly slower method of looking through our private array of commands to
  1366.     // locate the menu.
  1367.     
  1368.     MenuInfRec    mRec;
  1369.     
  1370.     mRec.macMenu = GetMenuHandle( menuID );
  1371.     
  1372.     if ( mRec.macMenu == NULL )
  1373.         FindMenuInfo( menuID, &mRec );
  1374.     
  1375.     return mRec.macMenu;
  1376. }
  1377.  
  1378.  
  1379. /*-------------------------------***  FINDMENUINFO  ***--------------------------------*/
  1380. /*
  1381. returns the info record for the given menu ID. This info is used to determine the right
  1382. dimming and disposal behaviour for the menu.
  1383. ---------------------------------------------------------------------------------------*/
  1384.  
  1385. void        ZMenuBar::FindMenuInfo( const short menuID, MenuInfRec* mRec )    
  1386. {
  1387.     short        n, i;
  1388.     MenuInfRec    mr;
  1389.     
  1390.     n = theMenus->CountItems();
  1391.     
  1392.     for( i = 1; i<= n; i++ )
  1393.     {
  1394.         theMenus->GetArrayItem( &mr, i );    
  1395.     
  1396.         if ( mr.menuID == menuID )
  1397.         {
  1398.             *mRec = mr;
  1399.             break;
  1400.         }
  1401.     }
  1402. }
  1403.  
  1404.  
  1405. /*-----------------------------***  GETMENUTITLERECT  ***------------------------------*/
  1406. /*
  1407. Get the title rect of a menu in the main menu bar
  1408. ---------------------------------------------------------------------------------------*/
  1409.  
  1410. void    ZMenuBar::GetMenuTitleRect( short menuID, Rect* tRect )
  1411. {
  1412.     // return the title rect (in GLOBAL coordinates) of the menu in the main bar with the
  1413.     // menu ID <menuID>. This returns the empty rect if no such menu was found or if the
  1414.     // menu is not in the main bar.
  1415.  
  1416.     Rect        tr = {0,0,0,0};
  1417.     MenuHandle    mH;
  1418.     mListHdl    mListH;
  1419.     short        i, iMax, tLength;
  1420.     GrafPtr        savePort, wPort;
  1421.     char        mhState;
  1422.     
  1423.     mListH = (mListHdl) LMGetMenuList();
  1424.     
  1425.     if ( mListH )
  1426.     {
  1427.         FailNILParam( mH = GetMenuHandle( menuID ));
  1428.         
  1429.         mhState = HGetState((Handle) mH );
  1430.         HLock((Handle) mH );
  1431.  
  1432.         // if this handle is in the menu list, then this must be in the main bar.
  1433.         // ASSUMPTION: hierarchical menus etc. are stored elsewhere.
  1434.     
  1435.         iMax = (((*mListH)->lastMOffset ) / sizeof( mListEntry )) - 2;
  1436.         
  1437.         for ( i = 0; i < iMax; i++ )
  1438.         {
  1439.             if ( mH == (*mListH)->mListItem[i].theMenu )
  1440.             {
  1441.                 // found it! Now set up the rect
  1442.                 
  1443.                 tr.top = 1;
  1444.                 tr.bottom = GetMBarHeight();
  1445.                 
  1446.                 tr.left = (*mListH)->mListItem[i].leftEdge;
  1447.                 
  1448.                 // the right edge is tricky- we need to get the title and figure the length
  1449.                 
  1450.                 GetPort( &savePort );
  1451.                 GetWMgrPort( &wPort );
  1452.                 SetPort( wPort );
  1453.                 
  1454.                 
  1455.                 tLength = StringWidth((*mH)->menuData ) + 10;
  1456.                 SetPort( savePort );
  1457.                 
  1458.                 tr.right = tr.left + tLength;
  1459.                 
  1460.                 break;
  1461.             }
  1462.         }
  1463.         
  1464.         HSetState((Handle) mH, mhState );    
  1465.     }
  1466.     
  1467.     *tRect = tr;
  1468. }
  1469.  
  1470.  
  1471. /*--------------------------***  SETZOOMSOURCETOCOMMAND  ***---------------------------*/
  1472. /*
  1473. set up the zoom source rect to the title of the menu containing the command, if any.
  1474. ---------------------------------------------------------------------------------------*/
  1475.  
  1476. void        ZMenuBar::SetZoomSourceToCommand( const long aCmd )
  1477. {
  1478.     short    menuID = 0, itemID = 0;
  1479.     
  1480.     FindCommand( aCmd, &menuID, &itemID );
  1481.     
  1482.     if ( menuID )
  1483.     {
  1484.         Rect    r;
  1485.         
  1486.         GetMenuTitleRect( menuID, &r );
  1487.         InsetRect( &r, 20, 4 );
  1488.         SetGlobalZoomSource( &r );
  1489.     }
  1490. }
  1491.